Перейти к основному содержимому

Структура и сборки Java-проектов

Разработчику Архитектору

Структуры проекта

Что такое пакет?

Пакетом (пространством имен) в Java называется структура вложенных по какому-то признаку папок с размещенными в них классами (интерфейсами, перечислениями, аннотациями), необходимыми проекту.

В Java используется пакетная структура – способ организации классов в папках, который помогает избежать конфликтов имён и группировать код логически. Это обязательная структура. Чтобы система понимала, что означает команда, указанная программистом в исходном коде в виде слова, нужно сообщить системе об использовании пакета, который содержит код-содержание этого слова:

«Дружище, налей мне молока!»
«А где мне взять молоко?».

Мы, люди, многие элементарные вещи уже изначально автоматизировали в себе и понимаем подсознательно. Мы прекрасно знаем, что молоко в холодильнике. Но программе нужно показать, что нам понадобится холодильник. Поэтому общение с программой будет как-то так:

«Дружище, запомни – вот холодильник. Там есть молоко».
«Дружище, налей мне молока!»
«Ок, вижу, молоко в холодильнике. Сейчас сделаю».

Вот поэтому код библиотеки или проекта формируется в составе пакета (package).

Пакетная структура решает две задачи: предотвращает конфликты имён между классами из разных источников и группирует связанный код для удобства навигации и поддержки.

Пакет — это механизм организации классов и интерфейсов в пространстве имен (namespace), который позволяет структурировать код, предотвращать конфликты имен и управлять доступом к компонентам программы. В Java пакеты соответствуют структуре директорий на диске.


Структура пакетов

Каждый Java-файл начинается с директивы package, указывающей полный путь к пакету.

package com.example.myapp.utils;

Компилятор использует этот путь для размещения скомпилированных .class файлов в соответствующих подкаталогах. Без указания пакета класс попадает в безымянное пространство имён, что допустимо только для простых учебных программ.

Физическая структура каталогов соответствует логическому имени пакета. Для класса с объявлением package com.example.app; компилятор ожидает размещение исходного файла по пути com/example/app/ClassName.java. Обратный слеш в имени пакета заменяется на разделитель каталогов операционной системы.

Имена пакетов строятся по обратному порядку домена организации, и хранятся в строгой структуре:

  • com/mycompany/project/Main.java – физический путь;
  • com.mycompany.project – путь в коде.

com – корень пакета. Только строчные буквы. Это префикс домена, обычно обратный интернет-домен компании или проекта. Нужен для уникальности пакетов в мире. Можно указывать своё, но рекомендуется использовать стандарт:

  • com – для коммерческих проектов;
  • org – для открытых проектов;
  • net, io, ru – тоже допустимы.

mycompany – название компании или автора, допустим, имя (если проект личный). Желательно делать уникальным. Только строчные буквы, несмотря на то что это будет имя или название – допустим, не Timur, а timur.

project – название проекта или модуля, библиотеки, подсистемы. Допустим, calculator, utils или как-то так. Только строчные буквы, название может быть любым.

Такой подход гарантирует глобальную уникальность имён пакетов. Два разработчика из разных компаний могут создать класс User, но их полные имена com.companyA.app.User и org.companyB.tool.User никогда не пересекутся.


Система сборки в Java

Современные Java-проекты используют системы сборки Maven и Gradle. Это инструменты, которые автоматизируют компиляцию кода, управление зависимостями (библиотеками), запуск тестов, создание готовых приложений и развёртывание (деплой).

Система сборки в Java представляет собой автоматизированный процесс преобразования исходного кода и ресурсов в исполняемый продукт, готовый к запуску или распространению. Этот процесс объединяет компиляцию файлов, проверку зависимостей, управление версиями и упаковку результатов в единый формат.

В экосистеме Java стандартом де-факто для управления сборкой является инструмент Maven. Он работает на основе декларативного подхода: разработчик описывает проект и его потребности в файле конфигурации pom.xml, а система сама выполняет необходимые шаги для получения результата.

Процесс сборки в Java проходит через несколько четко определенных стадий, которые выполняются последовательно. Каждая стадия отвечает за свою часть работы: от очистки старых файлов до финальной упаковки. Понимание этих этапов помогает эффективно диагностировать ошибки и оптимизировать работу.

  1. Очистка (Clean). Эта стадия удаляет содержимое директории target, которая содержит результаты предыдущих сборок. Сюда входят скомпилированные классы, сгенерированные отчеты, пакеты и временные файлы. Удаление этих данных гарантирует, что сборка начнется с чистого листа. Без этой стадии старые версии кода могут остаться в проекте, что приведет к некорректной работе программы или ошибкам при запуске. Команда mvn clean инициирует именно этот процесс.

  2. Сборка (Compile). На этой стадии компилятор Java обрабатывает исходные файлы .java, расположенные в папке src/main/java. Инструмент проверяет правильность синтаксиса, соответствие типов данных и наличие всех объявленных классов и методов. Если ошибок нет, генерируются файлы .class в папке target/classes. Эти файлы содержат байт-код, который является платформенно-независимым представлением программы. Компиляция также включает обработку аннотаций и генерацию вспомогательных файлов, если они требуются фреймворками.

  3. Тестирование (Test). Система копирует скомпилированные классы в специальную директорию target/test-classes и запускает набор тестов, находящихся в папке src/test/java. Тесты проверяют логику работы отдельных методов и классов. Если хотя бы один тест падает, сборка останавливается, и процесс завершается ошибкой. Это предотвращает выпуск продуктов с известными дефектами. Результаты тестирования сохраняются в виде отчетов в формате XML или HTML, которые можно проанализировать позже.

  4. Упаковка (Package). Скомпилированный код и ресурсы объединяются в архивный файл. Для стандартных приложений это JAR-файл (Java ARchive), содержащий классы и метаданные. Для веб-приложений создается WAR-файл (Web ARchive), включающий HTML, CSS, JavaScript и конфигурационные файлы. Упакованный артефакт становится готовым к развертыванию продуктом. В процессе упаковки также может происходить проверка целостности файлов и их подписывание цифровыми сертификатами.

  5. Установка (Install). Результат предыдущей стадии помещается в локальный репозиторий пользователя. Обычно это папка .m2/repository в домашней директории операционной системы. Установка делает библиотеку доступной для других проектов, работающих на этом компьютере. Если другой проект ссылается на эту библиотеку по групповому идентификатору, имени и версии, он найдет её в локальном хранилище без необходимости скачивания из сети.

  6. Развертывание (Deploy). Этот шаг выполняется только по требованию и подразумевает перемещение собранного артефакта в удаленный репозиторий. Развертывание позволяет команде разработчиков получить доступ к последней версии библиотеки через сеть. Удаленные репозитории часто используются внутри компаний для хранения внутренних инструментов или публикуются в интернете для широкого доступа.


Зависимости

Зависимости в Java — это внешние библиотеки, которые расширяют функциональность приложения. Они могут включать готовые реализации алгоритмов, инструменты для работы с сетью, базы данных или графическими интерфейсами.

В Java нет встроенной системы зависимостей (как в Go или Rust). Вместо этого есть инструменты сборки (Maven, Gradle), которые опираются на соглашения. Принцип координат в Maven (Maven Coordinates) определяет, что каждая зависимость имеет три значения:

groupId:artifactId:version

К примеру:

org.springframework:spring-core:5.3.27

Внутри это работает так:

  1. pom.xml декларирует всё, что нужно проекту.
  2. Репозиторий - это хранилище JAR-файлов (обычно Maven Central или частный Nexus/Artifactory).
  3. Локальный кэш (~/.m2/repository) хранит скачанные один раз JAR-файлы, на диске.
  4. Если A зависит от B, а B от C, то Maven сам скачает и C, и разрешит конфликты версий по стратегии "ближайший к корню". Это транзитивные зависимости.

То есть, зависимости не устанавливаются в систему, а привязываются к конкретному проекту.

Система сборки автоматически разрешает граф зависимостей, скачивая не только нужную библиотеку, но и все её внутренние зависимости.

Когда разработчик добавляет новую зависимость в pom.xml, система выполняет следующие действия:

  1. Проверяет локальный репозиторий на наличие указанной версии.
  2. Если версия найдена, она подключается к проекту.
  3. Если версия отсутствует, система обращается к удаленным репозиториям, перечисленным в конфигурации.
  4. Скачивает саму библиотеку и все её транзитивные зависимости (зависимости зависимостей).
  5. Сохраняет полученные файлы в локальный кэш для будущего использования.

Транзитивные зависимости создают сложную сеть связей между компонентами. Например, библиотека A зависит от библиотеки B, а библиотека B зависит от библиотеки C. При подключении A система автоматически подтянет B и C. Это упрощает жизнь разработчику, но иногда приводит к конфликтам версий, когда разные части проекта требуют разные версии одной и той же библиотеки.

Конфликты версий решаются механизмом приоритета. Maven выбирает первую зависимость, встретившуюся в дереве, или использует стратегию «ближайшей» зависимости. Если возникает конфликт, система выводит предупреждение в консоль, указывая, какая версия была выбрана и почему. Разработчик может явно указать желаемую версию в pom.xml, чтобы переопределить поведение по умолчанию.

Типы зависимостей определяются атрибутом scope:

  • compile: Стандартная зависимость, доступна на всех этапах.
  • test: Доступна только во время компиляции и запуска тестов.
  • provided: Предполагается, что среда выполнения предоставит библиотеку (например, сервер приложений).
  • runtime: Не нужна для компиляции, но требуется во время выполнения.
  • Система: Зависимость находится в локальной файловой системе и не скачивается из репозитория.

Maven

Как работает Maven?

Maven строится вокруг концепции Project Object Model (POM) — описания проекта в XML-формате. Файл pom.xml содержит метаданные, зависимости, плагины и настройки сборки. Проект следует стандартной структуре каталогов, что позволяет инструменту автоматизировать все этапы разработки без дополнительной конфигурации.

pom.xml содержит:

  • метаданные (название, версия, автор);
  • зависимости (библиотеки);
  • плагины (для сборки, тестирования);
  • настройки профилей (dev, prod).

POM является базовым модулем Maven. Это специальный XML-файл, который всегда хранится в базовой директории проекта и называется pom.xml. Файл POM содержит информацию о проекте и различных деталях конфигурации, которые используются Maven для создания проекта.

Maven делегирует компиляцию плагину maven-compiler-plugin. При выполнении фазы compile плагин:

  1. Сканирует каталог src/main/java рекурсивно
  2. Формирует список .java файлов
  3. Вызывает компилятор javac с параметрами:
    • -source и -target из свойств проекта
    • -encoding из project.build.sourceEncoding
    • -classpath на основе разрешённых зависимостей
  4. Размещает .class файлы в target/classes с сохранением структуры пакетов

Компиляция тестов происходит отдельно через фазу test-compile с использованием каталога src/test/java и размещением результатов в target/test-classes. Класспат для тестов включает основные классы и зависимости с областью test.

mvn может быть видна в глобальном терминале, но не в IDE-терминале, потому что переменные окружения загружаются при запуске терминала.

  • глобальный терминал (cmd, bash, zsh) читает PATH, где прописан Maven;
  • IDE (IntelliJ IDEA, VS Code, Eclipse) запускает свой встроенный терминал с тем окружением, с которым запущена сама IDE.

Если вы установили Maven после запуска IDE, то терминал IDE об этом не знает. Если вы запустили IDE не из терминала, она не наследует ваши shell-конфиги(.bashrc, .zshrc). В Windows разница между системными и пользовательскими переменными.

Лечится такое перезапуском IDE или ручным указанием PATH внутри терминала IDE.

Чтобы загрузить все зависимости, нужно написать команду:

mvn clean install

Здесь:

  • clean удаляет target/ (старые class-файлы);
  • install собирает проект и кладёт результат в локальный репозиторий.

Чтобы узнать точные названия и версии, нужно идти:

  • в Maven Central Search;
  • MVNRepository.com;
  • или искать внутри IDE, к примеру IDEA умеет искать зависимости по имени.

Версионность

Зависимости обновляются и выкладываются, теряют актуальность и устаревают, поэтому имеют версионные отличия. Когда мы обновляем зависимости, Maven смотрит в локальный кэш, и если зависимость релизная, то использует её. Если это SNAPSHOT, то проверяет по умолчанию раз в день, или при наличии флага -U:

mvn clean install -U

Флаг -U (--update-snapshots) — заставляет перекачать SNAPSHOT-зависимости (нестабильные версии).

Если зависимость не будет найдена, то Maven идёт в репозитории, объявленные в pom.xml / settings.xml.

Но Maven никогда не обновляет версию автоматически. Если нужно перейти с 1.0 на 1.1 — надо изменить pom.xml вручную.

Проверка репозиториев:

  • mvn help:effective-pom для итогового списка;
  • mvn dependency:tree для подтянутых зависимостей.

Если будет опечатка в группе или артефакте, то Maven не соберёт проект.

Неизбежные сложности с настройкой версий и зависимостей являются нормальной частью работы в Java. Экосистема огромна и разнообразна, поэтому периодическая «танцевальная» настройка окружения неизбежна. Разработчики сталкиваются с необходимостью обновлять JDK, менять версии Maven, корректировать pom.xml и решать конфликты версий.

Ключевой параметр в pom.xml говорит "компилируй так, чтобы работало на этой версии":

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>

Одной из распространенных проблем при сборке является несоответствие версий Java, Maven и зависимостей. Каждый компонент экосистемы имеет свои требования к версии языка и инструментария. Компилятор Java требует указания целевой версии (target) и версии исходного кода (source). Если эти параметры не соответствуют установленной версии JDK, возникнет ошибка.

Пример ошибки:

invalid target release: 23

Эта ошибка означает, что в файле pom.xml указана цель сборки на Java 23, но на компьютере установлена более старая версия (например, Java 17). Компилятор не может обработать синтаксис новой версии, так как он ему неизвестен. Решение заключается либо в обновлении JDK на компьютере до версии 23, либо в снижении целевой версии в настройках проекта до уровня установленного компилятора.

Настройки версий в pom.xml выглядят следующим образом:

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
</properties>

Эти свойства сообщают Maven, какую версию Java использовать для компиляции. Если проект использует функции, появившиеся в Java 17 (например, switch выражения или var), а целевая версия стоит на уровне 8, компилятор выдаст ошибку синтаксиса.

Минимальная версия Maven также важна. Новые версии Maven поддерживают новые возможности синтаксиса pom.xml и улучшают работу с зависимостями. Старые версии Maven могут не понимать современные конструкции, что приведет к ошибкам парсинга. Рекомендуется использовать актуальную стабильную версию инструмента.

Версии зависимостей должны соответствовать версии Java. Некоторые библиотеки требуют минимум Java 11 или Java 17 для корректной работы. Попытка запустить такую библиотеку на Java 8 вызовет ошибку времени выполнения NoSuchMethodError или ClassNotFoundException. Документация каждой библиотеки содержит информацию о требуемой версии платформы.

Если разработчик не знает точного названия, группы или версии зависимости, поиск должен проводиться через специализированные ресурсы. Основным источником информации является сайт Maven Central Search. На нем доступен полный индекс всех публичных библиотек. Пользователь вводит ключевые слова, описывающие функционал, который нужен (например, "JSON parsing" или "HTTP client"), и получает список подходящих вариантов.

Результаты поиска показывают:

  • groupId: Группа организации-разработчика.
  • artifactId: Имя самой библиотеки.
  • version: Доступные версии.
  • Описание: Краткое пояснение назначения библиотеки.

Альтернативный способ поиска — использование автозаполнения в IDE. При попытке подключить класс из неизвестной библиотеки среда разработки подсказывает возможные варианты импорта. Инструмент автоматически генерирует нужный блок <dependency> в pom.xml с правильными параметрами. Это избавляет от необходимости вручную искать названия и проверять их правильность.

Для уточнения версии полезно смотреть на страницу библиотеки в репозитории. Там отображается история изменений, дата последнего релиза и список известных проблем. Часто рекомендуется использовать последние стабильные версии, избегая бета-релизов, если проект не требует экспериментальных функций.

Если библиотека не найдена в публичных репозиториях, возможно, она находится в приватном хранилище компании или требует ручной установки в локальный репозиторий. В таком случае используется команда mvn install:install-file для добавления локального JAR-файла в систему.

Механизм обновления зависимостей базируется на регулярной сверке версий в локальном кэше с версиями в удаленных хранилищах. По умолчанию Maven проверяет наличие новых версий раз в день или при наличии специального флага. При запуске процесса система отправляет запрос к указанному в pom.xml репозиторию (например, Maven Central). Сервер возвращает список доступных версий артефакта.

Если локальная версия отличается от доступной, система предлагает обновление. Однако по умолчанию Maven не обновляет зависимости автоматически, если они уже присутствуют в кэше. Это сделано для стабильности сборки: случайное обновление библиотеки может сломать работающий код. Для форсирования проверки используется флаг -U или настройка <updateSnapshots>true</updateSnapshots> в файле конфигурации.

Проверка репозиториев происходит в строгом порядке. Сначала система обращается к локальному репозиторию. Если файл найден, он используется. Если нет, идет очередь к удаленным хранилищам, перечисленным в settings.xml или pom.xml. Обычно первым указывается официальный репозиторий Maven Central, затем корпоративные хранилища или приватные зеркала. Если ни один из источников не содержит требуемый файл, сборка прерывается с ошибкой.

При работе с SNAPSHOT-версиями (разработочными версиями) проверка происходит чаще. Эти версии помечаются суффиксом -SNAPSHOT и предполагают постоянные изменения. Система обязана проверять их наличие в репозитории каждый раз перед сборкой, чтобы получить актуальный код. Обычные релизные версии (1.0.0, 2.3.1) считаются неизменными и не требуют постоянной проверки после первого скачивания.

Команда mvn clean install -U выполняет полную очистку проекта, компиляцию, запуск тестов, упаковку и установку в локальный репозиторий, при этом принудительно проверяя наличие обновлений зависимостей в удаленных источниках. Это основной сценарий для приведения проекта в рабочее состояние.


CLI-команды Maven

Управление жизненным циклом:

  • mvn clean — удаляет каталог target со всеми скомпилированными классами и артефактами;
  • mvn compile — компилирует основной код из src/main/java в target/classes;
  • mvn test — компилирует и выполняет тесты из src/test/java, останавливает сборку при падении любого теста;
  • mvn package — собирает артефакт (JAR, WAR) в target/, проходя все предыдущие фазы;
  • mvn install — устанавливает артефакт в локальный репозиторий ~/.m2/repository;
  • mvn deploy — публикует артефакт в удалённом репозитории (Nexus, Artifactory).

Работа с зависимостями:

  • mvn dependency:tree — отображает иерархию транзитивных зависимостей;
  • mvn dependency:resolve — принудительно обновляет кэш зависимостей.

Полезные флаги:

  • -T 1C — параллельное выполнение по числу ядер процессора для ускорения сборки;
  • -DskipTests — пропускает компиляцию и запуск тестов;
  • -Dmaven.test.skip=true — пропускает только запуск тестов (компиляция выполняется).

pom.xml

Базовый файл pom.xml содержит следующие секции:

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">

<!-- Версия модели POM -->
<modelVersion>4.0.0</modelVersion>

<!-- Координаты проекта -->
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>

<!-- Общие свойства -->
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<!-- Зависимости -->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<!-- Плагины для расширения функционала -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

Секция <properties> задаёт переменные, используемые в других частях файла. Плагины в секции <build> расширяют стандартный жизненный цикл: компилятор настраивает версию Java, Surefire запускает тесты, Assembly создаёт исполняемые JAR-файлы.


Фазы Maven

Maven работает по жизненному циклу сборки проекта, который состоит из фаз. Каждая фаза представляет собой этап в процессе создания артефакта:

  • validate - проверяет корректность проекта и наличия необходимых данных;
  • compile - компилирует исходный код проекта;
  • test – запускает юнит-тесты;
  • package - упаковывает скомпилированный код в JAR/WAR/SO и т.д.;
  • verify - выполняет проверки после упаковки перед установкой;
  • install - устанавливает артефакт в локальное хранилище Maven;
  • deploy - передаёт артефакт в удалённый репозиторий (например, Nexus, Artifactory).

Можно вызвать одну из этих фаз через команды, к примеру:

mvn compile # компиляция
mvn package # сборка артефакта
mvn install # установка в локальный репозиторий

Выполнение команды mvn package запускает все предшествующие фазы в порядке их следования. Пропустить тесты можно флагом -DskipTests.

Файлы проекта распределены по строгой стандартной структуре:

ПутьНазначение
/ПроектОбщая папка проекта
/Проект/pom.xmlГлавный конфигурационный файл (Maven)
/Проект/srcДиректория, где хранится весь исходный код Java-проекта
/Проект/src/mainОсновной код проекта
/Проект/src/main/javaИсходные файлы Java, организованные в пакетной структуре
/Проект/src/main/java/com/mycompany/projectПример пакета в проекте
/Проект/src/main/resourcesРесурсы: конфигурационные файлы, локализация, изображения, CSS, JS и другие не-кодовые файлы
/Проект/src/testТестовый код, не включаемый в финальную сборку
/Проект/src/test/javaТестовые классы (например, с использованием JUnit, TestNG)
/Проект/src/test/resourcesКонфигурация и ресурсы, используемые при тестировании
/Проект/targetКаталог для автоматически генерируемых файлов (сборка, артефакты, временные файлы)

Структура каталогов Maven

my-app/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── app/
│ │ │ └── Main.java
│ │ └── resources/
│ │ └── application.properties
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ └── app/
│ │ └── MainTest.java
│ └── resources/
│ └── test-config.properties
└── target/
├── classes/
├── test-classes/
└── my-app-1.0.0.jar

Каталог target создаётся автоматически при сборке и содержит все промежуточные и финальные артефакты. Его содержимое можно удалить командой mvn clean.


Управление зависимостями в Maven

Maven автоматизирует загрузку библиотек через централизованную систему координат. Каждая зависимость описывается тремя атрибутами:

  • groupId — организация или домен владельца библиотеки
  • artifactId — имя библиотеки
  • version — конкретная версия

Пример объявления зависимости в pom.xml:

<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>

При первой сборке Maven проверяет локальный репозиторий ~/.m2/repository. Если артефакт отсутствует, инструмент обращается к центральному репозиторию по адресу https://repo.maven.apache.org/maven2, скачивает библиотеку и все её транзитивные зависимости, сохраняя их локально. Последующие сборки используют кэшированные файлы без сетевых запросов.

Транзитивные зависимости разрешаются автоматически. Если библиотека A зависит от B версии 2.0, а проект напрямую использует B версии 1.5, Maven применяет правила разрешения конфликтов для выбора единой версии.

Maven разрешает зависимости в три этапа:

  1. Анализ секции <dependencies> в pom.xml
  2. Рекурсивное извлечение транзитивных зависимостей из метаданных артефактов
  3. Применение правил разрешения конфликтов версий:
    • Ближайший путь в дереве зависимостей
    • Явное указание версии в <dependencyManagement>
    • Порядок объявления в родительском POM

Зависимости классифицируются по области действия (scope):

  • compile — доступны во время компиляции и выполнения (значение по умолчанию)
  • test — только для тестов, не попадают в финальный артефакт
  • runtime — не нужны для компиляции, требуются при запуске
  • provided — предоставляются окружением выполнения (например, сервлеты в веб-контейнере)

Артефакт Maven

Артефакт в Maven – это результат сборки проекта, например, JAR-файл, который содержит код, библиотеки, ресурсы и метаданные.

Каждый артефакт имеет уникальный идентификатор, состоящий из элементов:

  • groupId – com.example – организация или домен;
  • artifactId – my-app – имя проекта или библиотеки;
  • version – 1.0.0., 2.1.3-SNAPSHOT – версия.

Пример:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>

Это будет ссылка на артефакт:

org.springframework.boot:spring-boot-starter-web:2.7.0

Артефакты бывают следующих видов:

  • .jar – обычные Java-библиотеки;
  • .war – веб-приложения;
  • .pom – описание проекта;
  • .aar – Android-библиотеки..

Репозиторий

Репозиторий – это место, где хранятся артефакты. Они могут храниться локально (локальный репозиторий), в общедоступном репозитории Maven (центральный), и в удалённом (частном), для приватных артефактов.

При первом использовании зависимости Maven скачивает её из центрального репозитория, и сохраняет в локальный. При повторном использовании уже берёт из локального. То есть, добавляя зависимость в pom.xml, мы скачиваем и сохраняем локально в папку ~/.m2/repository.


Пошаговое создание проекта Maven

  1. Установите Apache Maven и убедитесь, что команда mvn доступна в терминале.
  2. Выполните генерацию шаблона проекта:
    mvn archetype:generate \
    -DgroupId=com.example \
    -DartifactId=my-app \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false
  3. Перейдите в созданную директорию:
    cd my-app
  4. Откройте файл src/main/java/com/example/App.java и замените содержимое:
    package com.example;

    public class App {
    public static void main(String[] args) {
    System.out.println("Привет из Maven!");
    }
    }
  5. Соберите проект:
    mvn package
  6. Запустите приложение:
    java -cp target/my-app-1.0.0.jar com.example.App

Команда mvn package компилирует исходный код, запускает тесты и формирует JAR-файл в каталоге target. Maven автоматически вызывает компилятор javac с правильными путями к исходникам и зависимостям, управляет класспатом и генерирует манифест для артефакта.

Важно: вы можете столкнуться с проблемой, когда в IDEA у вас будут отображаться ошибки в стиле Cannot resolve symbol 'javafx', хотя если вы выполните mvn javafx:run то всё ок. Проблема в том, что начиная с Java 11, JavaFX больше не входит в состав JDK.

Maven загружает его как обычную зависимость, но IDEA требует дополнительной настройки для распознавания модулей. IDEA иногда "не замечает" новые зависимости после изменения pom.xml, особенно если проект не был переимпортирован.

Самый простой вариант - открыть правую панель Maven, и нажать иконку Reload All Maven Projects.


Gradle

Как работает Gradle?

Gradle – система автоматической сборки, построенная с учетом принципов Maven, но предоставляющая дополнительные возможности на языках Groovy и Kotlin вместо традиционной XML-образной формы представления конфигурации проекта. Это позволяет писать логику сборки как обычный код: использовать переменные, циклы, условия и функции.

Основные компоненты проекта Gradle:

  • build.gradle или build.gradle.kts — основной файл сборки
  • settings.gradle — настройки мультипроектной структуры
  • gradle/ — обёртка Gradle для кроссплатформенного запуска без глобальной установки

Gradle вводит концепцию задач (Задачи) как атомарных единиц работы. Задачи могут зависеть друг от друга, формируя направленный ациклический граф выполнения. Инкрементальная сборка анализирует изменения входных файлов и пересобирает только затронутые модули, что ускоряет повторные сборки.

Принцип разделения на src/main и src/test аналогичен Maven, но конфигурация хранится в build.gradle, а не в pom.xml. Отличие есть в папке для автоматически сгенерированных файлов – в Maven они хранятся по пути /target, а в Gradle - /build.

Gradle использует задачу compileJava из плагина java. Процесс аналогичен Maven, но с дополнительными оптимизациями:

  1. Инкрементальная компиляция — анализ изменений на уровне классов
  2. Демон компиляции — фоновый процесс, сохраняющий состояние между запусками
  3. Параллельная компиляция — использование нескольких ядер процессора

Настройка компилятора происходит через блок toolchain, который автоматически выбирает установленную JDK подходящей версии или загружает её при отсутствии. Это устраняет зависимость от переменной окружения JAVA_HOME.


build.gradle

Конфигурация на Groovy DSL:

plugins {
id 'java'
id 'application'
}

group = 'com.example'
version = '1.0.0'

java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}

application {
mainClass = 'com.example.App'
}

repositories {
mavenCentral()
}

dependencies {
implementation 'org.apache.commons:commons-lang3:3.12.0'
testImplementation 'junit:junit:4.13.2'
}

Конфигурация на Kotlin DSL (build.gradle.kts):

plugins {
java
application
}

group = "com.example"
version = "1.0.0"

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}

application {
mainClass.set("com.example.App")
}

repositories {
mavenCentral()
}

dependencies {
implementation("org.apache.commons:commons-lang3:3.12.0")
testImplementation("junit:junit:4.13.2")
}

Оба формата эквивалентны по функционалу. Kotlin DSL обеспечивает строгую типизацию и автодополнение в IDE, Groovy DSL — более лаконичный синтаксис.


CLI-команды Gradle

Управление жизненным циклом:

  • ./gradlew clean — удаляет каталог build;
  • ./gradlew build — выполняет полную сборку (компиляция, тестирование, создание артефакта);
  • ./gradlew compileJava — компилирует только основной код;
  • ./gradlew test — запускает только тесты;
  • ./gradlew jar — создаёт JAR-файл без выполнения тестов;
  • ./gradlew publishToMavenLocal — устанавливает артефакт в локальный кэш Maven (~/.m2/repository);
  • ./gradlew run — запускает приложение (требует плагин application).

Информационные команды:

  • ./gradlew Задачи --all — отображает все доступные задачи с описанием;
  • ./gradlew dependencies — выводит дерево зависимостей.

Полезные флаги и режимы:

-x test — пропускает выполнение тестов (например, ./gradlew build -x test); --parallel — включает параллельное выполнение задач; --continuous (или -t) — непрерывный режим: сборка перезапускается при изменении исходных файлов.


Сборка в Gradle

Команда gradle build запускает стандартный жизненный цикл:

  1. classes — компиляция основного кода
  2. testClasses — компиляция тестов
  3. test — выполнение тестов
  4. jar — создание JAR-артефакта
  5. assemble — агрегация всех артефактов

Результаты сохраняются в каталоге build:

build/
├── classes/
│ ├── java/main/ # скомпилированные классы
│ └── java/test/ # скомпилированные тесты
├── libs/
│ └── my-app-1.0.0.jar
├── reports/tests/ # отчёты о тестировании
└── tmp/ # временные файлы

Gradle отслеживает состояние входных и выходных файлов каждой задачи. При повторной сборке без изменений исходного кода задачи помечаются как актуальные (UP-TO-DATE) и пропускаются.

Gradle использует конфигурации вместо областей действия:

  • implementation — зависимости для компиляции и выполнения, скрыты от потребителей библиотеки
  • api — зависимости, видимые потребителям (для библиотек)
  • testImplementation — зависимости только для тестов
  • runtimeOnly — зависимости только для выполнения

Преимущество подхода — контроль видимости транзитивных зависимостей. При использовании implementation зависимости не «утекают» в проекты, использующие вашу библиотеку, что снижает риск конфликтов версий.

Разрешение зависимостей происходит лениво — только при выполнении задач, требующих класспат. Это ускоряет запуск Gradle для операций, не связанных со сборкой (например, gradle Задачи).


Запуск проекта Gradle

Gradle предоставляет несколько способов запуска приложения:

  • Через задачу run плагина application:
    gradle run
  • Через обёртку (без глобальной установки Gradle):
    ./gradlew run # Linux/macOS
    gradlew.bat run # Windows
  • Создание исполняемого JAR с манифестом:
    jar {
    manifest {
    attributes 'Main-Class': 'com.example.App'
    }
    }
    Запуск после сборки:
    java -jar build/libs/my-app-1.0.0.jar

Обёртка gradlew скачивает указанную версию Gradle при первом запуске и использует её для всех последующих сборок, гарантируя воспроизводимость независимо от окружения разработчика.


Сравнение Maven и Gradle

Краткое сравнение Maven и Gradle

КритерийMavenGradle
Тип конфигурацииXMLGroovy/Kotlin DSL
Файл конфигурацииpom.xmlbuild.gradle

Команды в Maven и Gradle

КритерийMavenGradle
Компиляцияmvn compilegradle compileJava
Запуск тестовmvn testgradle test
Сборка JAR/WARmvn packagegradle build
Установка в локальный кэшmvn installgradle publishToMavenLocal
Очистка кэшаmvn cleangradle clean
Запуск приложенияmvn exec:javagradle run
Пропустить тестыmvn install -DskipTestsgradle build -x test
Обновить зависимостиmvn dependency:resolvegradle dependencies
Создать проектmvn archetype:generategradle init
Показать список задачmvn help:describegradle Задачи

Аналогичные секции в pom.xml и build.gradle

MavenGradle
<groupId>group
<artifactId>Имя проекта (из settings.gradle)
<version>version
<properties>Переменные в ext {}
<dependencies>dependencies {}
maven-compiler-pluginjava.toolchain

Gradle стал стандартом для:

  • Android-разработки — официальная система сборки Android Studio
  • Spring Boot — генератор проектов start.spring.io предлагает Gradle по умолчанию
  • Мультипроектных систем — поддержка иерархии проектов с общими зависимостями
  • Кастомных сценариев — публикация в репозитории, генерация документации, деплой на серверы

Гибкость скриптов позволяет интегрировать любые внешние инструменты: запускать утилиты командной строки, обрабатывать файлы, взаимодействовать с веб-сервисами.


Структура проекта Gradle

my-app/
├── build.gradle # или build.gradle.kts
├── settings.gradle
├── gradlew
├── gradlew.bat
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── App.java
│ │ └── resources/
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ └── AppTest.java
│ └── resources/
└── build/ # генерируется автоматически

Каталог gradle/wrapper содержит обёртку для кроссплатформенного запуска. Файл gradle-wrapper.properties указывает версию Gradle:

distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip

Пошаговое создание проекта Gradle

  1. Установите Gradle или используйте обёртку из другого проекта.
  2. Инициализируйте новый проект:
    gradle init --type java-application \
    --project-name my-app \
    --package com.example \
    --dsl groovy
  3. Система предложит выбрать вариант тестового фреймворка (JUnit 4, JUnit 5, TestNG). Выберите JUnit Jupiter (JUnit 5).
  4. Откройте сгенерированный файл src/main/java/com/example/App.java:
    package com.example;

    public class App {
    public String getGreeting() {
    return "Привет из Gradle!";
    }

    public static void main(String[] args) {
    System.out.println(new App().getGreeting());
    }
    }
  5. Соберите и запустите проект:
    ./gradlew build
    ./gradlew run

Gradle автоматически скачает зависимости из mavenCentral(), скомпилирует код с использованием JDK 17 (согласно настройкам toolchain), запустит тесты и выполнит основной класс. Все этапы логируются с указанием затраченного времени и статуса каждой задачи.


Работа с Java без систем сборки

Разработка на Java возможна без Maven и Gradle.

Минимальный рабочий процесс:

  1. Создайте структуру каталогов вручную:
    mkdir -p src/com/example/app
    mkdir -p bin
  2. Напишите класс src/com/example/app/Main.java:
    package com.example.app;

    public class Main {
    public static void main(String[] args) {
    System.out.println("Прямая компиляция");
    }
    }
  3. Скомпилируйте вручную:
    javac -d bin -sourcepath src src/com/example/app/Main.java
  4. Запустите:
    java -cp bin com.example.app.Main

Для проектов с зависимостями потребуется вручную скачивать JAR-файлы, формировать класспат через флаг -cp и отслеживать совместимость версий. Сборка артефактов потребует использования утилиты jar с ручным созданием манифеста.

Такой подход применим для учебных задач, скриптов или микропроектов. Для коммерческой разработки, командной работы и поддержки сложных систем ручное управление становится непрактичным из-за роста объёма рутинных операций и риска ошибок. Системы сборки автоматизируют повторяющиеся действия, обеспечивают воспроизводимость и интеграцию с инструментами непрерывной интеграции.


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).